/* ===================

AX2E (AXESCON XACML 2.0 Engine) is the Java authorization engine, which implements OASIS XACML 2.0 standard.
Copyright (C) 2007 AXESCON LLC

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA


Contact: AXESCON LLC - info{at}axescon.com

 =================== */
package axescon.xacml.engine;

import axescon.xacml.api.*;
import axescon.xacml.model.ctx.XacmlCtx;
import axescon.xacml.model.policy.AttributeValue;
import axescon.xacml.model.policy.GenericMatch;
import axescon.xacml.model.policy.Target;
import axescon.xacml.model.policy.TargetPart;

/**
 * @author argyn
 *         Date: Jun 25, 2005
 *         Time: 2:16:46 PM
 *         Responsibilities: represents XACML PDP's target matching logic.
 *         Other classes use this class, whenever target matching is required, e.g.
 *         when applicable policies are looked up in the store
 *         <p/>
 *         see axescon.xacml 2.0 spec ch. 7.6. Target evaluation
 */
public class TargetMatcherImpl implements TargetMatcher{


    /**
     * matches target t with XacmlCtx req using context handler ch
     *
     * @param t   target of a policy, policy set or rule
     * @param req request
     * @param ch  context hanlder
     * @return true if target matched
     * @throws TargetMatchIndeterminateException
     *
     */
    public boolean match(Target t, XacmlCtx req, CtxHandler ch) throws
            XacmlException, TargetMatchIndeterminateException {
        if (t == null) return true;

        boolean ret = match(t.getSubjectArray(), req, ch);
        ret = ret && match(t.getResourceArray(), req, ch);
        ret = ret && match(t.getEnvironmentArray(), req, ch);
        return ret && match(t.getActionArray(), req, ch);
    }


    public boolean match(TargetPart[] x, XacmlCtx req, CtxHandler ch) throws
            XacmlException, TargetMatchIndeterminateException {
        if (x == null) return true;

        for (int i = 0; i < x.length; i++) {
            TargetPart a = x[i];
            if (match(a, req, ch)) return true;

        }
        return false;
    }

    public boolean match(TargetPart x, XacmlCtx req, CtxHandler ch) throws
            XacmlException, TargetMatchIndeterminateException {
        TargetMatchIndeterminateException indeterminate = null;
        try {
            for (int i = 0; i < x.getMatchArray().length; i++) {
                GenericMatch a = x.getMatchArray()[i];
                if (!match(a, req, ch)) return false;

            }
        } catch (TargetMatchIndeterminateException e) {
            indeterminate = e;
        }
        if (indeterminate != null) throw indeterminate;
        return true;
    }


    /**
     * see axescon.xacml 2.0 ch 7.5. Match evaluation
     *
     * @param x
     * @param req
     * @param ch
     * @return
     * @throws TargetMatchIndeterminateException
     *
     */
    public boolean match(GenericMatch x, XacmlCtx req, CtxHandler ch) throws
            XacmlException, TargetMatchIndeterminateException {
        //TODO: implement this

        AttributeValue arg0 = x.getAttributeValue();
        Liquid arg1 = null;
        try {
            if (x.isSetAttributeDesignator())
                arg1 = x.getAttributeDesignator().eval(ch, req, getExpressionEvaluator());
            else
                arg1 = x.getAttributeSelector().eval(ch, req, getExpressionEvaluator());
        } catch (ExpressionEvalIndeterminateException e) {
            throw new TargetMatchIndeterminateException(e, e.getErrCode());
        }


        ExpressionEvalIndeterminateException indeterminate = null;
        for (int i = 0; arg1.getBagUnsafe()!=null && i < arg1.getBagUnsafe().length; i++) {
            AttributeValue av = arg1.getBagUnsafe()[i];
            try {
                if (getFnFactory().match(x.getMatchId(), arg0, av)) return true;
            } catch (ExpressionEvalIndeterminateException e) {
                indeterminate = e;
            }
        }
        if (indeterminate == null) return false;
        throw new TargetMatchIndeterminateException(indeterminate, indeterminate.getErrCode());
    }


    private FnFactory fnFactory;

    public FnFactory getFnFactory() {
        return fnFactory;
    }

    public void setFnFactory(FnFactory fnFactory) {
        this.fnFactory = fnFactory;
    }

    private ExpressionEvaluator expressionEvaluator;

    public ExpressionEvaluator getExpressionEvaluator() {
        return expressionEvaluator;
    }

    public void setExpressionEvaluator(ExpressionEvaluator expressionEvaluator) {
        this.expressionEvaluator = expressionEvaluator;
    }


    public void setDefaults() throws XacmlException {
        setExpressionEvaluator(new ExpressionEvaluatorImpl(true));
        setFnFactory(new BaseFnFactory(true));
    }

    public TargetMatcherImpl() throws XacmlException {
        this(false);
    }

    public TargetMatcherImpl(boolean setDefaults) throws XacmlException {
        if (setDefaults) setDefaults();
    }

}
